import cv2
import os
import time
import random
import threading
from ctypes import *


def adb_exec(cmd):
    os.system(os.path.split(os.path.realpath(__file__))[0] + '\\adb\\adb.exe ' + cmd)


def count_distance(pt1, pt2):
    return ((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) ** 0.5


class Jumper:
    def __init__(self, speed, debug):
        self.speed = speed
        self.debug = debug
        self.image = None
        self.height = 0
        self.width = 0
        self.origin = None

        if self.debug:
            print 'debug mode'
        self.player = cv2.cvtColor(cv2.imread(os.path.split(os.path.realpath(__file__))[0] + '\\player.png'),
                                   cv2.COLOR_RGB2GRAY)
        adb_exec('shell mkdir -p /sdcard/jumper')

    def do_action(self, distance):
        action_time = int(distance * 100 / self.speed)
        print 'distance', distance, 'speed', self.speed, 'time', action_time
        if 4000 > action_time > 50:
            x1 = 300 + random.randint(0, 100)
            y1 = 300 + random.randint(0, 100)
            x2 = 400 + random.randint(0, 100)
            y2 = 400 + random.randint(0, 100)
            adb_exec('shell input swipe %d %d %d %d %d' % (x1, y1, x2, y2, action_time))
        else:
            print 'time invalid'

    def get_screen(self):
        name = str(time.time())
        mobile_path = '/sdcard/jumper/%s.png' % name
        pc_path = 'images/%s.png' % name
        adb_exec('shell screencap -p ' + mobile_path)
        adb_exec('pull %s %s' % (mobile_path, pc_path))
        adb_exec('shell rm -f ' + mobile_path)
        self.load_img(pc_path)
        if self.debug is False:
            os.remove(pc_path)

    def load_img(self, img_path):
        self.image = cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_RGB2GRAY)
        print ''
        print 'load image finish, path is', img_path
        self.height, self.width = self.image.shape
        if self.debug:
            self.origin = self.image

    def find_current_point(self):
        method = cv2.TM_CCOEFF_NORMED
        res = cv2.matchTemplate(self.image, self.player, method)
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
        # print 'current point max val', max_val

        # img_tmp = self.image.copy()
        # bottom_right = (max_loc[0] + self.player.shape[::-1][0], max_loc[1] + self.player.shape[::-1][1])
        # cv2.rectangle(img_tmp, max_loc, bottom_right, 255, 2)
        # cv2.imshow('match template', img_tmp)

        current_point = (max_loc[0] + self.player.shape[::-1][0] / 2, max_loc[1] + self.player.shape[::-1][1] - 17)
        location_right = (current_point[0] * 2 > self.width)
        return current_point, location_right


class JumperAuto(Jumper):
    def __init__(self, speed, opencv_weight=3, symmetry_weight=1, use_white_point=True, debug=False):
        Jumper.__init__(self, speed, debug)
        self.opencv_weight = opencv_weight
        self.symmetry_weight = symmetry_weight
        self.use_white_point = use_white_point

        self.white_point = cv2.cvtColor(cv2.imread(os.path.split(os.path.realpath(__file__))[0] + '\\white.png'),
                                        cv2.COLOR_RGB2GRAY)

    def start(self, loop=True):
        if loop:
            print 'start with auto mode'
        else:
            print 'start with one time mode'
        while True:
            # load_img('C:\\Users\\BlueSun\\Desktop\\1.png')
            self.get_screen()
            distance = self.get_distance()
            self.do_action(distance)
            time.sleep(1.8 + random.random())
            if loop is not True:
                break

    def remove_invalid_part(self, location_right):
        start_width = 0
        end_width = self.width / 2

        if location_right is False:
            start_width = self.width / 2
            end_width = self.width

        for h in range(0, self.height / 3):
            for w in range(0, self.width):
                self.image[h, w] = 0

        for h in range(0, self.height):
            for w in range(self.width / 2 - start_width, self.width * 3 / 2 - end_width):
                self.image[h, w] = 0

        # cv2.imshow('remove invalid', self.image)

    def find_new_point(self, location_right):

        start_width = 0
        end_width = self.width / 2 - 1

        if location_right is False:
            start_width = self.width / 2 + 1
            end_width = self.width

        self.image = cv2.GaussianBlur(self.image, (5, 5), 0)
        self.image = cv2.Canny(self.image, 9, 10)
        # cv2.imshow('canny', self.image)

        top_point_h = 0
        point_w = 0

        for top_point_h in range(self.height / 3 + 1, self.height / 2):
            for point_w in range(start_width, end_width):
                if self.image[top_point_h, point_w] > 0:
                    break
            else:
                continue
            break

        # print 'top point', (point_w, top_point_h)

        bottom_point_h = 0
        for bottom_point_h in range(top_point_h + 10, self.height * 2 / 3):
            if self.image[bottom_point_h, point_w] > 0:
                break

        # print 'bottom point', (point_w, bottom_point_h)

        new_point = (point_w, (top_point_h + bottom_point_h) / 2)
        return new_point

    def find_while_point(self):
        method = cv2.TM_CCOEFF_NORMED
        res = cv2.matchTemplate(self.image, self.white_point, method)
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

        if self.debug:
            bottom_right = (max_loc[0] + self.white_point.shape[::-1][0], max_loc[1] + self.white_point.shape[::-1][1])
            cv2.rectangle(self.origin, max_loc, bottom_right, 255, 1)
            cv2.imshow('match template', self.origin)
            cv2.waitKey(0)
            cv2.destroyAllWindows()

        current_point = (max_loc[0] + self.white_point.shape[::-1][0] / 2 + 1,
                         max_loc[1] + self.white_point.shape[::-1][1] / 2 + 1)
        return current_point, max_val

    def show_point(self, point, width):
        if self.debug is False:
            return
        top_left = (point[0] - 5, point[1] - 5)
        bottom_right = (point[0] + 5, point[1] + 5)
        cv2.rectangle(self.origin, top_left, bottom_right, 255, width)
        cv2.imshow('show point', self.origin)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    def get_distance(self):
        current_point, location_right = self.find_current_point()
        print 'current point', current_point
        # print 'location right', location_right
        self.remove_invalid_part(location_right)
        opencv_point = self.find_new_point(location_right)
        print 'opencv point', opencv_point
        symmetry_point = (int(self.width * 1.04) - current_point[0], int(self.height * 1.021) - current_point[1])
        print 'symmetry point', symmetry_point
        new_point = ((opencv_point[0] * self.opencv_weight + symmetry_point[0] * self.symmetry_weight) /
                     (self.opencv_weight + self.symmetry_weight),
                     (opencv_point[1] * self.opencv_weight + symmetry_point[1] * self.symmetry_weight) /
                     (self.opencv_weight + self.symmetry_weight))

        self.show_point(new_point, 2)

        if self.use_white_point:
            white_point, res = self.find_while_point()
            dis = count_distance(new_point, white_point)
            print 'white point', white_point, dis, res
            if (dis < 50 and res > 0.4) or (dis < 75 and res > 0.45) or (dis < 100 and res > 0.5) or res > 0.6:
                new_point = white_point
        print 'new point', new_point

        self.show_point(new_point, 5)
        return count_distance(current_point, new_point)


class JumperManual(Jumper):

    class Point(Structure):
        _fields_ = [('x', c_long),
                    ('y', c_long)]

    def __init__(self, speed, title_height=25, debug=False):
        Jumper.__init__(self, speed, debug)

        self.current_ctrl_key = 0
        self.last_ctrl_key = 0
        self.current_shift_key = 0
        self.last_shift_key = 0
        self.next_point = (0, 0)
        self.current_point = (0, 0)
        self.distance = 0
        self.title_height = title_height
        self.dll = windll.LoadLibrary('user32.dll')

    def start(self, loop=True):
        self.start_capture()
        if loop:
            print 'start with manual mode'
        else:
            print 'start with one time mode'
        while True:
            self.get_screen()
            self.process_image()
            self.current_point, res = self.find_current_point()
            top_left = (self.current_point[0] - 5, self.current_point[1] - 5)
            bottom_right = (self.current_point[0] + 5, self.current_point[1] + 5)
            cv2.rectangle(self.image, top_left, bottom_right, 255, 2)

            self.current_point = (self.current_point[0] + 4, self.current_point[1] + self.title_height)
            print 'start point', self.current_point
            cv2.imshow('image', self.image)
            cv2.moveWindow('image', 0, 0)
            cv2.waitKey(0)
            cv2.destroyAllWindows()
            self.do_action(self.distance)
            time.sleep(1)
            if loop is not True:
                break

    def process_image(self):
        self.image = self.image[self.height / 3: self.height * 2 / 3, :]

    def start_capture(self):
        self.do_capture()
        threading.Timer(0.05, self.start_capture).start()

    def get_point(self):
        pt = JumperManual.Point()
        self.dll.GetCursorPos(byref(pt))
        return pt.x, pt.y

    def do_capture(self):
        self.current_shift_key = (self.dll.GetKeyState(16) & 0x1000)
        if self.current_shift_key != 0 and self.last_shift_key == 0:
            self.next_point = self.get_point()
            print 'end point', self.next_point
            self.distance = count_distance(self.next_point, self.current_point)
            self.dll.keybd_event(13, 0, 0, 0)

        self.last_shift_key = self.current_shift_key

        self.current_ctrl_key = (self.dll.GetKeyState(17) & 0x1000)
        if self.current_ctrl_key != 0 and self.last_ctrl_key == 0:
            self.current_point = self.get_point()
            print 'start point', self.current_point

        self.last_ctrl_key = self.current_ctrl_key


if __name__ == '__main__':
    speed_input = 74
    while True:
        print ''
        print 'current speed %d' % speed_input
        print '-------MAIN MENU-------'
        print '[1] --- auto mode'
        print '[2] --- manual mode'
        print '[3] --- change speed'
        print '[0] --- exit'
        s = raw_input('Enter your input: ')
        print ''
        if s == '1':
            JumperAuto(speed_input, debug=False).start()
        elif s == '2':
            JumperManual(speed_input, debug=False).start()
        elif s == '3':
            try:
                speed_input = input("Please input your speed, current %d:\n" % speed_input)
            except SyntaxError:
                pass
        elif s == '0':
            exit(0)
        else:
            print 'input error'
